﻿using FCN;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2.util
{
    public delegate float DbackDelegate(int indexx,int x,int y,float xdata,float ydata,float tdata);
    public delegate float D4dbackDelegate(int indexx,int indexyy, int x, int y, float xdata, float ydata, float tdata);
    public   class MSELoss
    {
      public  float calcRMSE(float [] Data, float[] Data2)
        {
            float fSum = 0;
            int Num = Data.GetLength(0);
            for (int i = 0; i < Num; ++i)
            {
                fSum += (Data[i] - Data2[i]) * (Data[i] - Data2[i]);
            }
            return (float)Math.Sqrt(fSum / (float)Num);
        }
       // np.sum(np.square(x - y)) / x.size
        public float calcRMSE(float[,] Data, float[,] Data2)
        {
            float fSum = 0;
            int X = Data.GetLength(0);
            int y = Data.GetLength(1);
            for (int i = 0; i < X; ++i)
            {
                for (int j = 0; j < y; ++j)
                    fSum += (Data[i,j] - Data2[i,j]) * (Data[i,j] - Data2[i,j]);
            }
            return (float)Math.Sqrt(fSum / (float)(X*y));
        }
    
        public float calcRMSE(Matrix[,] Data, Matrix[,] Data2)
        {
          
            float fSum = 0;
            int X = Data.GetLength(0);
            int y = Data.GetLength(1);
            for (int i = 0; i < X; ++i)
            {
                for (int j = 0; j < y; ++j)
                    fSum += calcRMSE(Data[i,j].values, Data2[i,j].values);
            }
            return (float)fSum / (float)(X * y);
        }

        public float calcRMSE(Matrix[] Data, Matrix[] Data2)
        {

            float fSum = 0;
            int X = Data.GetLength(0);
          
            for (int i = 0; i < X; ++i)
            {
             
                    fSum += calcRMSE(Data[i].values, Data2[i].values);
            }
            return (float)fSum / (float)(X );
        }

        internal float[] MESBasic(Matrix[,] dW,int outnum)
        {
            float[] outputB = new float[outnum];
            for (var s = 0; s < outnum; s++)
            {
                float sum = 0f;
                for (var ss = 0; ss < dW.GetLength(0); ss++)
                {
                    sum += Sum(dW[ss, s]);
                }
                outputB[s] = sum ;
            }
            return outputB;
        }
       float Sum(Matrix input)
        {
            float sum = 0f;
            int m = input.values.GetLength(0);
            int n = input.values.GetLength(1);
            for (int i = 0; i < m ; i++)
            {
                for (int j = 0; j < n ; j++)
                {

                    sum+= input.values[0,0];
                    
                }
            }
            return sum;
        }

      
        public Matrix[] MESBackwradconv( Matrix[] input, Matrix[] temp, Matrix[] temptag, DbackDelegate dd,int Kszie,int stride,int padding)
        {
            Matrix[] temptt = new Matrix[temp.GetLength(0)];
            for (var x = 0; x < temp.GetLength(0); x++)
            {
                float[,] sum2 = null;
                for (var s = 0; s < input.GetLength(0); s++)
                {

                 
              

                        float[,] aatemp = MESBackwradconv(input[s].values, temptag[x].values, temp[x].values, Kszie, stride, padding,dd,x);
                      


                 
                    if (sum2 == null)
                    {

                        sum2 = aatemp;
                    }
                    else

                        sum2 = Matrix.MatrixAdd(sum2, aatemp).values;


                }
                if (temptt[x] == null)
                    temptt[x] = new Matrix();
                temptt[x].values = Matrix.divide(sum2, temp.GetLength(0));

            }





            return temptt;
        }

        public Matrix[,] MESBackwradconv(Matrix[,] input, Matrix[,] temp, Matrix[,] temptag, int Kszie, int stride, int padding, D4dbackDelegate D4D = null)
        {
            Matrix[,] temptt = new Matrix[temp.GetLength(0), temp.GetLength(1)];
            for (var x = 0; x < temp.GetLength(0); x++)
            {
                for (var y = 0; y < temp.GetLength(1); y++)
                {
                    float[,] sum2 = null;
                  
                        //for (var s2 = 0; s2 < input.GetLength(1); s2++)
                        {
                            for (var st = 0; st < input.GetLength(0); st++)
                            {
                                float[,] sum = null;
                                for (var st2 = 0; st2 < input.GetLength(0); st2++)
                                {


                                    float[,] aatemp = MESBackwradconv(input[st, st2].values, temptag[x,y].values, temp[x, y].values, Kszie, stride, padding, D4D,x,y);
                                    if (sum == null)
                                        sum = aatemp;
                                    else
                                        sum = Matrix.MatrixAdd(sum, aatemp).values;


                                }
                                if (sum2 == null)
                                {

                                    sum2 = Matrix.divide(sum, input.GetLength(0)* input.GetLength(1));
                                }
                                else

                                    sum2 = Matrix.MatrixAdd(sum2, Matrix.divide(sum, input.GetLength(0)* input.GetLength(1))).values;
                            }
                        }

                    if (temptt[x,y] == null)
                        temptt[x,y] = new Matrix();
                    temptt[x,y].values = Matrix.divide(sum2, temp.GetLength(0) * temp.GetLength(1));
                }

            }





            return temptt;
        }
        //public Matrix[,] MESBackwrad(Matrix[,] input, Matrix[,] temp, Matrix[,] temptag)
        //{
        //    Matrix[,] temptt = new Matrix[temp.GetLength(0), temp.GetLength(1)];
        //    for (var x = 0; x < temp.GetLength(0); x++)
        //    {
        //        for (var y = 0; y < temp.GetLength(1); y++)
        //        {
        //            float[,] sum2 = null;

        //            //for (var s2 = 0; s2 < input.GetLength(1); s2++)
        //            {
        //                for (var st = 0; st < input.GetLength(0); st++)
        //                {
        //                    float[,] sum = null;
        //                    for (var st2 = 0; st2 < input.GetLength(0); st2++)
        //                    {


        //                        float[,] aatemp = MESBackwrad(input[st, st2].values, temptag[x, y].values, temp[x, y].values);
        //                        if (sum == null)
        //                            sum = aatemp;
        //                        else
        //                            sum = Matrix.MatrixAdd(sum, aatemp).values;


        //                    }
        //                    if (sum2 == null)
        //                    {

        //                        sum2 = Matrix.divide(sum, input.GetLength(0) * input.GetLength(1));
        //                    }
        //                    else

        //                        sum2 = Matrix.MatrixAdd(sum2, Matrix.divide(sum, input.GetLength(0) * input.GetLength(1))).values;
        //                }
        //            }

        //            if (temptt[x, y] == null)
        //                temptt[x, y] = new Matrix();
        //            //  temptt[x, y].values = Matrix.divide(sum2, temp.GetLength(0) * temp.GetLength(1));
        //            temptt[x, y].values = sum2;
        //        }

        //    }





        //    return temptt;
        //}
        //public Matrix[] MESBackwrad(Matrix[] input, Matrix[] temp, Matrix[] temptag, int kernel = 3, int stride = 1,int padding = 0)
        //{
        //    Matrix[] temptt = new Matrix[temp.GetLength(0)];
        //    for (var x = 0; x < temp.GetLength(0); x++)
        //    {
               
        //            float[,] sum2 = null;

                    
        //            for (var st = 0; st < input.GetLength(0); st++)
        //            {




        //                float[,] aatemp = MESBackwrad(input[st].values, temptag[x].values, temp[x].values, kernel, stride,padding);
        //                if (sum2 == null)
        //                    sum2 = aatemp;
        //                else
        //                    sum2 = Matrix.MatrixAdd(sum2, aatemp).values;



        //            }
                    

        //            if (temptt[x] == null)
        //                temptt[x] = new Matrix();
        //            //  temptt[x, y].values = Matrix.divide(sum2, temp.GetLength(0) * temp.GetLength(1));
        //            temptt[x].values = sum2;
                

        //    }





        //    return temptt;
        //}
        //internal Matrix[,] MESDconv(Matrix[,] temp,int kernel,  int stride, int padding)
        //{
        //    Matrix[,] tempyy = new Matrix[temp.GetLength(0), temp.GetLength(1)];
        //    float[,] Ytea = null;
        //    for (var i = 0; i < temp.GetLength(0); i++)
        //    {
        //        for (var j = 0; j < temp.GetLength(1); j++)
        //        {
        //            if (Ytea == null)
        //            { Ytea=new float[temp[i, j].values.GetLength(0), temp[i, j].values.GetLength(1)];
        //                Ytea=initytag(Ytea,1);
        //            }
        //            if (tempyy[i, j] == null)
        //                tempyy[i, j] = new Matrix();
                    
        //            tempyy[i, j].values= convnValid(temp[i, j].values, Ytea, stride, 0);
        //        }
        //    }
        //    return tempyy;
        //}
        //internal Matrix[] MESDconv(Matrix[] temp, int kernel, int stride, int padding)
        //{
        //    Matrix[] tempyy = new Matrix[temp.GetLength(0)];
        //    float[,] Ytea = null;
        //    for (var i = 0; i < temp.GetLength(0); i++)
        //    {
                
        //            if (Ytea == null)
        //            {
        //                Ytea = new float[temp[i].values.GetLength(0), temp[i].values.GetLength(1)];
        //                Ytea = initytag(Ytea, 1);
        //            }
        //            if (tempyy[i] == null)
        //                tempyy[i] = new Matrix();
               
        //            tempyy[i].values = MESDconvolution(temp[i].values, Ytea, stride, padding);
                
        //    }
        //    return tempyy;
        //}
        float[,] initytag(float[,] Ytea,int value)
        {
            for (var i = 0; i < Ytea.GetLength(0); i++)
            {
                for (var j = 0; j < Ytea.GetLength(1); j++)
                {
                    Ytea[i, j] = value;
                }
            }
            return Ytea;
        }
        public  float[,] convnValid(float[,] matrix, float[,] kernel, int stride, int p)
        {
           // kernel = rot180(kernel);
            int m = matrix.GetLength(0);
            int n = matrix.GetLength(1);
            int km = kernel.GetLength(0);
            int kn = kernel.GetLength(1);
            //var row = ((x - x2) + 2 * p) / stride + 1;
            //var col = ((y - y2) + 2 * p) / stride + 1;
            int kns = ((n - kn) + 2 * p) / stride + 1;// n - kn + 1;
            int kms = ((m - km) + 2 * p) / stride + 1;//m - km + 1;
            float[,] outMatrix = new float[kns, kms];

            for (int i = 0; i < kms; i++)
            {
                for (int j = 0; j < kns; j++)
                {
                    float sum = 0.0f;
                    for (int ki = i - p; ki < km - p; ki++)
                    {
                        for (int kj = j - p; kj < kn - p; kj++)
                        {
                            if (i + ki < 0 || j + kj < 0)
                                sum += 0;
                            else if (i + ki >= km || j + kj >= km)
                                sum += 0;
                            else
                            sum += matrix[i + ki, j + kj];
                        }
                    }
                    outMatrix[i, j] = sum / (km * kn);

                }
            }
            return outMatrix;
        }
         public float[,] MESDconvolution(float[,] values, float[,] YTAG, int stride, int padding = 0)
        {
            
                
            var x = values.GetLength(0);
            var y = values.GetLength(1);
            var x2 = YTAG.GetLength(0);
            var y2 = YTAG.GetLength(1);
            var p = padding;
            //Ho=(H−F+2×P)/S+1
            var row = ((x - x2) + 2 * p) / stride + 1;
            var col = ((y - y2) + 2 * p) / stride + 1;
            float[,] temp = new float[row, col];
            var nx = 0;

            for (var i = 0 - p; i <= x - x2 + p; i = i + stride)
            {
                var ny = 0;
                for (var j = 0 - p; j <= y - y2 + p; j = j + stride)
                {
                    if (temp.GetLength(0) > nx && temp.GetLength(1) > ny)
                        //var sum = 0.0f;
                        for (var i2 = 0; i2 < x2; i2++)
                            for (var j2 = 0; j2 < y2; j2++)
                            {
                                if (i + i2 < 0 || j + j2 < 0)
                                    temp[nx, ny] += 0;
                                else if (i + i2 >= x || j + j2 >= y)
                                    temp[nx, ny] += 0;
                                else
                                {
                                    temp[nx, ny] += values[i + i2, j + j2] * YTAG[i2, j2];
                                    if (values[i + i2, j + j2] > 0)
                                    { }
                                }
                            }
                    temp[nx, ny] = (float)temp[nx, ny] / (x2 * y2);
                    // temp[nx, ny] = Math.Max(ReLU, temp[nx, ny] + bias);
                    ny++;
                }
                nx++;
            }

            return temp;
        }
        //public Matrix MESBackwradconv(Matrix input, Matrix temp, Matrix temptag, int Kszie, int stride, int padding)
        //{


        //    Matrix temptt = new Matrix();
        //      float[,] aatemp = MESBackwradconv(input.values, temptag.values, temp.values, Kszie, stride, padding);

        //    temptt.values = aatemp;
              
        //    return temptt;
        //}
        //float[,] MESBackwrad(float[,] values, float[,] Ytag, float[,] temptt, int kernel=3,int stride=1, int padding=0)
        //{


        //    var x = values.GetLength(0);
        //    var y = values.GetLength(1);
        //    var x2 = Ytag.GetLength(0);
        //    var y2 = Ytag.GetLength(1);
        //    var p = padding;
        //    //Ho=(H−F+2×P)/S+1
        //    var row = ((x - x2) + 2 * p) / stride + 1;
        //    var col = ((y - y2) + 2 * p) / stride + 1;
        //  //  float[,] temp = new float[row, col];
        //    float[,] tempss = new float[x, y];
        //    var nx = 0;

        //    for (var i = 0 - p; i <= x - x2 + p; i = i + stride)
        //    {
        //        var ny = 0;
        //        for (var j = 0 - p; j <= y - y2 + p; j = j + stride)
        //        {
                    
        //                //var sum = 0.0f;
        //                for (var i2 = 0; i2 < x2; i2++)
        //                    for (var j2 = 0; j2 < y2; j2++)
        //                    {
        //                    if (i + i2 < 0 || j + j2 < 0)
        //                        continue;
        //                    else if (i + i2 >= x || j + j2 >= y)
        //                        continue;
        //                    else
        //                    {
        //                        //temp[nx, ny] += -2 * (values[i + i2, j + j2]) * (mtag[i2, j2] - temptt[i2, j2]);
        //                        tempss[i + i2, j + j2] += -2 * (values[i + i2, j + j2]) * (Ytag[i2, j2] - temptt[i2, j2]);
        //                    }
        //                    }
                   
        //            // temp[nx, ny] = Math.Max(ReLU, temp[nx, ny] + bias);
        //            ny++;
        //        }
        //        nx++;
        //    }

        //    return tempss;
        //}
        float[,] MESBackwradconv(float[,] X, float[,] Ytag, float[,] temp,int wsize, int stride, int padding = 0, DbackDelegate dd=null,int indexx=0)
        {
            
            float[,] W = new float[wsize, wsize];

            W=convolutionMES(X, Ytag, temp, stride, padding,dd,indexx);
           // float[,] W2= convnValidMES(X, Ytag, temp, stride, padding);
            return W;
        }
        float[,] MESBackwradconv(float[,] X, float[,] Ytag, float[,] temp, int wsize, int stride, int padding = 0, D4dbackDelegate dd = null, int indexx = 0,int indexy=0)
        {

            float[,] W = new float[wsize, wsize];

            W = convolutionMES(X, Ytag, temp, stride, padding, dd, indexx, indexy);
            // float[,] W2= convnValidMES(X, Ytag, temp, stride, padding);
            return W;
        }
        public float[,] convolutionMES(float[,] values, float[,] mtag, float[,] temptt, int stride, int padding = 0, D4dbackDelegate dd = null, int indexx = 0,int indexy=0)
        {

            var x = values.GetLength(0);
            var y = values.GetLength(1);
            var x2 = mtag.GetLength(0);
            var y2 = mtag.GetLength(1);
            var p = padding;
            //Ho=(H−F+2×P)/S+1
            var row = ((x - x2) + 2 * p) / stride + 1;
            var col = ((y - y2) + 2 * p) / stride + 1;
            float[,] temp = new float[row, col];
            //float[,] tempss = new float[x, y];
            var nx = 0;
           
            for (var i = 0 - p; i <= x - x2 + p; i = i + stride)
            {
                var ny = 0;
                for (var j = 0 - p; j <= y - y2 + p; j = j + stride)
                {
                    if (temp.GetLength(0) > nx && temp.GetLength(1) > ny)
                        //var sum = 0.0f;
                        for (var i2 = 0; i2 < x2; i2++)
                            for (var j2 = 0; j2 < y2; j2++)
                            {
                                if (i + i2 < 0 || j + j2 < 0)
                                    temp[nx, ny] += 0;
                                else if (i + i2 >= x || j + j2 >= y)
                                    temp[nx, ny] += 0;
                                else
                                {


                                    if (dd != null)
                                        temp[nx, ny] += dd(indexx, indexy, i2, j2, values[i + i2, j + j2], mtag[i2, j2], temptt[i2, j2]);
                                    else
                                        temp[nx, ny] += -2 * (values[i + i2, j + j2]) * (mtag[i2, j2] - temptt[i2, j2]);
                                    // tempss[i + i2, j + j2] += -2 * (values[i + i2, j + j2]) * (mtag[i2, j2] - temptt[i2, j2]);
                                }
                                //if (i + i2 == 3 && j + j2 == 3)
                                //{ nxcount = nxcount + 1; }
                            }
                    temp[nx, ny] = (float)Math.Round(temp[nx, ny] / (x2 * y2), 4);
                    // temp[nx, ny] = Math.Max(ReLU, temp[nx, ny] + bias);
                    ny++;
                }
                nx++;
            }

            return temp;
        }
        public float[,] convolutionMES(float[,] values, float[,] mtag, float[,] temptt, int stride, int padding = 0, DbackDelegate dd=null, int indexx = 0)
        {

            var x = values.GetLength(0);
            var y = values.GetLength(1);
            var x2 = mtag.GetLength(0);
            var y2 = mtag.GetLength(1);
            var p = padding;
            //Ho=(H−F+2×P)/S+1
            var row = ((x - x2) + 2 * p) / stride + 1;
            var col = ((y - y2) + 2 * p) / stride + 1;
            float[,] temp = new float[row, col];
            //float[,] tempss = new float[x, y];
            var nx = 0;
          
            for (var i = 0 - p; i <= x - x2 + p; i = i + stride)
            {
                var ny = 0;
                for (var j = 0 - p; j <= y - y2 + p; j = j + stride)
                {
                    if (temp.GetLength(0) > nx && temp.GetLength(1) > ny)
                        //var sum = 0.0f;
                        for (var i2 = 0; i2 < x2; i2++)
                            for (var j2 = 0; j2 < y2; j2++)
                            {
                                if (i + i2 < 0 || j + j2 < 0)
                                    temp[nx, ny] += 0;
                                else if (i + i2 >= x || j + j2 >= y)
                                    temp[nx, ny] += 0;
                                else
                                {


                                    if (dd != null)
                                        temp[nx, ny] += dd(indexx,  i2,  j2, values[i + i2, j + j2], mtag[i2, j2], temptt[i2, j2]);
                                    else
                                        temp[nx, ny] += -2 * (values[i + i2, j + j2]) * (mtag[i2, j2] - temptt[i2, j2]);
                                    // tempss[i + i2, j + j2] += -2 * (values[i + i2, j + j2]) * (mtag[i2, j2] - temptt[i2, j2]);
                                }
                                //if (i + i2 == 3 && j + j2 == 3)
                                //{ nxcount = nxcount + 1; }
                            }
                    temp[nx, ny] =(float)Math.Round( temp[nx, ny] / (x2 * y2),4);
                    // temp[nx, ny] = Math.Max(ReLU, temp[nx, ny] + bias);
                    ny++;
                }
                nx++;
            }

            return temp;
        }
    }
}
